/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#ifndef GIMI_MULTIDATA_H
#define GIMI_MULTIDATA_H

#include "gimiutils.h"
#include "connector.h"

namespace gimi {

// Define for friend class
class MultiBuffer;
  
//// Structures and typedefs for search-tree that can be used to define 
//// redirections.
  
typedef std::pair< int, int > t_clientIdPair;

typedef std::vector< t_clientIdPair > t_clientIdUpdateList;
typedef t_clientIdUpdateList::iterator t_clientIdUpdateListIterator;
typedef t_clientIdUpdateList::const_iterator t_clientIdUpdateListConstIterator;
  
typedef std::set< unsigned int > t_queueIdentifiers;
typedef t_queueIdentifiers::iterator t_queueIdentifiersIterator;
typedef t_queueIdentifiers::const_iterator t_queueIdentifiersConstIterator;

/**
 * Helper method for looping through a queue and adding them to a redirections
 * list.
 *
 * \return Number of found queues.
 */
int writeQueueIdentifiersToRedirections(t_redirectionList &redirections,
                                        const t_queueIdentifiers &queues,
                                        const int majorTypeId = -1,
                                        const int minorTypeId = -1,
                                        const int senderId = -1);

struct SenderInfo {
  SenderInfo(const std::string &sName);
  
  std::string senderName; /// For renewing senderId's if they are reset in tcpHub.
  t_queueIdentifiers queues;
};

typedef std::map< uint32_t, SenderInfo > t_senderInformation;
typedef t_senderInformation::iterator t_senderInformationIterator;
typedef t_senderInformation::const_iterator t_senderInformationConstIterator;
typedef std::pair< uint32_t, SenderInfo > t_senderInformationPair;

struct MinorInfo {
  MinorInfo();
  t_senderInformation senderInformation;
  
  t_queueIdentifiers queues;
};

typedef std::map< int, MinorInfo > t_minorRedirectMap;
typedef t_minorRedirectMap::iterator t_minorRedirectMapIterator;
typedef t_minorRedirectMap::const_iterator t_minorRedirectMapConstIterator;
typedef std::pair< int, MinorInfo > t_minorRedirectMapPair;

struct MajorInfo {
  MajorInfo();
  t_minorRedirectMap minorInformation;
  
  t_queueIdentifiers queues;
};

typedef std::map< int, MajorInfo > t_redirectMap;
typedef t_redirectMap::iterator t_redirectMapIterator;
typedef t_redirectMap::const_iterator t_redirectMapConstIterator;
typedef std::pair< int, MajorInfo > t_redirectMapPair;

/**
 * Helper methods for looping through and writing down redirections.
 */
int writeSenderQueuesToRedirections(t_redirectionList &redirections,
                                    const t_senderInformation &senders,
                                    const int majorTypeId,
                                    const int minorTypeId,
                                    const int senderId);

int writeMinorQueuesToRedirections(t_redirectionList &redirections,
                                   const t_minorRedirectMap &minorTypes,
                                   const bool exactTypes,
                                   const int majorTypeId,
                                   const int minorTypeId,
                                   const int senderId);

/**
 * \class MultiData
 * Maintains information about buffers to which data needs to be sent.
 */
class MultiData {
  friend class MultiBuffer;
  
  public:
    MultiData(Connector *connPtr);
    ~MultiData();
    
    /**
     * Reserves a queue-number for use.
     * 
     * \return Queue number to use, or 0 if we ran out of numbers (very unlikely,
     * but should be checked against.)
     */
    unsigned int reserveQueue();
    
    /**
     * Release queue-number. Also checks for and removes any redirections to
     * specified queue.
     * 
     * \return True if queue was found and removed, false if queue did not exist.
     */
    bool releaseQueue(const unsigned int queueIdentifier);
    
    /**
     * \warning If senderName is given, senderId should be up to date also!
     * \return True if adding succeeded, false if given combination already existed.
     */
    bool addRedirection(const unsigned int queueIdentifier,
                        const int majorTypeId,
                        const int minorTypeId = -1,
                        const std::string &senderName = "",
                        const int senderId = -1);
    
    /**
     * 
     * \return True if removing succeeded, false if given combination did not exist.
     */
    bool removeRedirection(const unsigned int queueIdentifier,
                           const int majorTypeId,
                           const int minorTypeId = -1,
                           const int senderId = -1);
    /**
     *
     * \return Number of found redirections.
     */
    int listRedirections(t_redirectionList &redirections,
                         const bool exactTypes = false,
                         const int majorTypeId = -1,
                         const int minorTypeId = -1,
                         const int senderId = -1);
    
    /**
     * Goes through every name in redirections and refreshes it's senderId
     * information
     *
     * \return Number of clientnames that could not be updated.
     */
    int renewRedirectionSenderIds();
    
    /**
     * \return True if class does not contain any multipliers.
     */
    bool isEmpty() const;
    
  protected:
    /**
     * Checks if given type & sender needs to be copied to several queues and
     * lists the queues.
     * 
     * \param[out] queueList List of id's for queues to which data needs to be
     * copied to.
     * \param[in] majorTypeId
     * \param[in] minorTypeId
     * \param[in] senderId
     * \return Number of additional queues for data. If 0, use only default
     * queue.
     */
    int listQueues(t_queueIdentifiers &queueList,
                   const int majorTypeId,
                   const int minorTypeId,
                   const int senderId);
    
  private:
    // Prevent use of copy constructors
    MultiData(const MultiData& md);
    const MultiData& operator= (const MultiData& right);
    
    t_queueIdentifiers reservedQueues;
    unsigned int nextQueueNumber;
    
    t_redirectMap redirectInformation; ///< Redirections
    ownMutexHandle redirectMutex;
    Connector* connectionPtr;
};

} // end namespace gimi

#endif // GIMI_MULTIDATA_H
